﻿using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using System.Transactions;

namespace Apex.ASPNETCore.UnitOfWork;

/// <summary>
/// 在Filter中，通过反射拿到action中用到的DbContext（反射拿到Attribute中标注的信息）
/// </summary>
public class UnitOfWorkFilter : IAsyncActionFilter
{
    /// <summary>
    /// 获取UnitOfWorkAttribute标注
    /// </summary>
    /// <param name="actionDesc">控制器action信息</param>
    /// <returns></returns>
    private static UnitOfWorkAttribute? GetUoWAttr(ActionDescriptor actionDesc)
    {
        // 是否 控制器动作方法(ControllerAction)
        var caDesc = actionDesc as ControllerActionDescriptor;
        if (caDesc == null)
        {
            return null;
        }

        /*
         * UnitOfWorkAttribute可以标注在类或方法
         * 尝试从控制器获取UnitOfWorkAttribute，
         * 如果控制器上没有UnitOfWorkAttribute，
         * 尝试从操作中获取UnitOfWorkAttribute
        */
        var uowAttr = caDesc.ControllerTypeInfo
            .GetCustomAttribute<UnitOfWorkAttribute>();
        if (uowAttr != null)
        {
            return uowAttr;
        }
        else
        {
            return caDesc.MethodInfo
                .GetCustomAttribute<UnitOfWorkAttribute>();
        }
    }

    /// <summary>
    /// 在执行控制器操作时调用的异步方法。该方法用于处理 Unit of Work (工作单元) 操作过滤器的逻辑
    /// </summary>
    /// <param name="context">包含有关当前操作的信息的执行上下文</param>
    /// <param name="next">执行下一个过滤器的委托</param>
    /// <returns>表示异步操作的任务</returns>
    public async Task OnActionExecutionAsync(ActionExecutingContext context,
        ActionExecutionDelegate next)
    {
        // 获取控制器action上标注的 UnitOfWorkAttribute 特性
        var uowAttr = GetUoWAttr(context.ActionDescriptor);

        // 如果没有标注 UnitOfWorkAttribute，则继续执行下一个过滤器
        if (uowAttr == null)
        {
            await next();
            return;
        }

        // 使用 TransactionScope 以确保事务性操作
        using TransactionScope txScope = new(TransactionScopeAsyncFlowOption.Enabled);

        // 创建一个列表来存储 DbContext 实例
        List<DbContext> dbCtxs = new List<DbContext>();

        // 遍历 UnitOfWorkAttribute 中指定的 DbContext 类型
        foreach (var dbCtxType in uowAttr.DbContextTypes)
        {
            // 通过 HttpContext 的 RequestServices 获取服务提供程序以确保获取与请求相关的Scope实例
            var sp = context.HttpContext.RequestServices;
            // 从服务提供程序中获取 DbContext 实例，并添加到列表中
            DbContext dbCtx = (DbContext)sp.GetRequiredService(dbCtxType);
            dbCtxs.Add(dbCtx);
        }

        // 执行下一个过滤器或操作
        var result = await next();

        // 如果操作没有异常，则提交事务并保存更改到数据库
        if (result.Exception == null)
        {
            var isSuccess = (result.Result is OkObjectResult) || (result.Result is OkResult); // api返回值是OK() 或者Ok("")
            if (isSuccess)
            {
                foreach (var dbCtx in dbCtxs)
                {
                    await dbCtx.SaveChangesAsync();
                }
                txScope.Complete(); // 提交事务
            }
        }
    }
}